#include <queue>
#include <pthread.h>
template <class T>
class BlockQueue
{
private:
    // 类里面使用的接口
    bool IsFull()
    {
        return _block_queue.size() == _cap;
    }
    bool IsEmpty()
    {
        return _block_queue.size() == 0;
    }

public:
    BlockQueue(int cap) : _cap(cap), _consumer_wait_num(0), _productor_wait_num(0)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_productor, nullptr);
        pthread_cond_init(&_consumer, nullptr);
    }
    void Enqueue(T &in) // 生产者用的接口
    {
        pthread_mutex_lock(&_mutex);
        // 加锁保证生产者和生产者，生产者和消费者之间的互斥关系
        // 临界区
        while (IsFull())
        {
            // 满了，就放到生产者条件变量指定的等待队列中等待
            // 等待时记得释放锁
            _productor_wait_num++;
            pthread_cond_wait(&_productor, &_mutex);
            _productor_wait_num--;
        }
        // 没满，插入数据后唤醒消费者消费
        _block_queue.push(in);
        if (_consumer_wait_num > 0)
            pthread_cond_signal(&_consumer);
        pthread_mutex_unlock(&_mutex);
    }
    void Pop(T *out) // 消费者用的接口
    {
        pthread_mutex_lock(&_mutex);
        // 加锁保证消费者和消费者，生产者和消费者之间的互斥关系
        // 临界区
        while (IsEmpty())
        {
            // 没有数据可以消费，在消费者条件变量下进行等待
            _consumer_wait_num++;
            pthread_cond_wait(&_consumer, &_mutex);
            _consumer_wait_num--;
        }
        // 有数据，消费数据后唤醒生产者生产数据
        *out = _block_queue.front();
        _block_queue.pop();
        if (_productor_wait_num > 0)
            pthread_cond_signal(&_productor);
        pthread_mutex_unlock(&_mutex);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_productor);
        pthread_cond_destroy(&_consumer);
    }
protected:
    std::queue<T> _block_queue; // 阻塞队列
    int _cap;                   // 阻塞队列的大小
    pthread_mutex_t _mutex;     // 用于保护阻塞队列
    pthread_cond_t _productor;  // 专门给生产者的条件变量
    pthread_cond_t _consumer;   // 专门给消费者的条件变量
    int _productor_wait_num;
    int _consumer_wait_num;
};